<?php


class BLoader
{
    /**
     * 工作路径别名
     *
     */
    const ALIASE_CALL =                     '>';

    /**
     * 根目录别名
     *
     */
    const ALIASE_PROJECT =                  '.';

    /**
     * 框架路径别名
     *
     */
    const ALIASE_FRAMEWORK =                '..';
    /**
     * 别名
     *
     */
    private static $_aliases =              array();
    /**
     * 类实例
     *
     * @static
     * @access private
     * @var array
     */
    private static $_instances =            array();

    /**
     * 目录分隔符
     *
     */
    const SPACE_DIR =    DIRECTORY_SEPARATOR;
    /**
     * 文件后缀
     *
     */
    const FILE_EXTNAME = '.php';
    /**
     * 类名前缀
     *
     */
    const CLASS_PREFIX = 'B';
    /**
     * 设置/取得别名
     *
     * @static
     * @access public
     * @param string  $name 名称
     * @param string  $path 别名指向的路径
     * @return mixed
     */
    static function aliase($name = null, $path = null)
    {
        if (is_null($name)){
            return self::$_aliases;
        }
        if (is_null($path)){
            return isset(self::$_aliases[$name]) ? self::$_aliases[$name] : null;
        }
        if (!isset(self::$_aliases[$name])) {
            self::$_aliases[$name] = $path;
        }
        return true;
    }
	/**
	 * 加载文件
	 *
	 * @access public
	 * @static
	 * @param string $namespace
	 * @param int $mode
	 * @param bool $once
	 */
	static function Import ($namespace, $once = true)
	{
		$file = self::PathName($namespace);
		if (!$file || !file_exists($file)) {
			return false;
		}
		return $once ? include_once($file) : include($file);
	}

    /**
     * 取得指定路径的类实例
     *
     * @static
     * @access public
     * @param string  $namespace
     * @return mixed
     */
    static function instance($namespace)
    {
        $instance = self::$_instances[$namespace];
        if (is_null($instance)){
            try {
                self::import($namespace);
                $class = self::ClassName($namespace);
                $instance = new $class;
            } catch (BLoaderException $e){
                throw $e;
            }
        }
        self::$_instances[$namespace] = $instance;
        return $instance;

    }

	/**
	 * 根据命名空间获取对象名称
	 *
	 * @access public
	 * @static
	 * @param string $namespace
	 * @return string
	 */
	static function ClassName($namespace)
	{
	    if (!$parsed = self::_Parse($namespace)) {
	    	return false;
	    }
        $name = str_replace(array('\\','/'), '_', trim($parsed['name'], '/'));
        return self::CLASS_PREFIX. ucfirst(basename($name, self::FILE_EXTNAME) );
	}

	/**
	 * 命名空间到实际路径
	 *
	 * @access public
	 * @static
	 * @param string $namespace
	 * @return string
	 */
	static function PathName($namespace)
	{
        if (!$parsed = self::_Parse($namespace)) {
        	return false;
        }
        return $parsed['path'];
	}


    /**
     * 根据某调用路径取得该调用(顺序)亲缘树
     *
     * @static
     * @access public
     * @param boolean $readable 是否要求链中每个文件都可读
     * @return array
     */
    static function familyTree($readable = true)
    {
        $path = rtrim(self::aliase(self::ALIASE_CALL), self::SPACE_DIR);
        $root = rtrim(self::aliase(self::ALIASE_PROJECT), self::SPACE_DIR);
        $paths = array();
        $parent = true;
        while ($parent && strpos($path,$root) !== false){
            $tmp = $path;
            if (!$readable || is_readable($tmp)){
                $paths[] = $tmp;
            }
            $parent = $path != $root;
            $path = dirname($path);
        }
        return array_reverse(array_filter($paths));
    }

	/**
	 * 解析命名空间
	 *
     * @access private
     * @param string $namespace
     * @return mixed
	 */
	private static function _Parse($namespace)
	{
		$namespace = str_replace(array('\\','/','_'), self::SPACE_DIR, $namespace);
        $namespaces = explode(self::SPACE_DIR, $namespace);
        $aliases = self::aliase();
        if (!is_array($namespaces) || !$aliases[$namespaces[0]]) {
            return false;
        }
        $aliase = $namespaces[0];
        unset($namespaces[0]);
        $namespace = implode(self::SPACE_DIR, $namespaces);
        $extend = pathinfo($namespace);
		if (!$extend['extension'] && substr($namespace,-1) != self::SPACE_DIR) {
			$namespace.= self::FILE_EXTNAME;
		}
		return array('path'=> $aliases[$aliase]. self::SPACE_DIR.$namespace, 'namespace' => $aliase, 'name' => $namespace );
	}

}

?>